// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ir

import (
	"cmd/compile/internal/base"
	"cmd/internal/src"
)

// 节点可以实现Orig和SetOrig方法来
// 维护指向节点“未写入”形式的指针。
// 如果一个节点没有实现OrigNode，它就是它自己的Orig。
// None
// 请注意，SepCopy和Copy都有兼容的定义
// 使用不实现OrigNode的节点：这样的节点
// 是它自己的起源，在这种情况下，这就是双方想要返回的
// 无论如何（无条件复制，仅当输入
// 也是它自己的源代码，但是如果输出没有实现
// OrigNode，则输入也不会，使条件为真）。
type OrigNode interface {
	Node
	Orig() Node
	SetOrig(Node)
}

// origNode可以嵌入到节点中，使其实现origNode。
type origNode struct {
	orig Node `mknode:"-"`
}

func (n *origNode) Orig() Node     { return n.orig }
func (n *origNode) SetOrig(o Node) { n.orig = o }

// Orig返回n的“原始”节点。
// 如果n实现OrigNode，则Orig返回n.Orig（）。
// 否则Orig返回n本身。
func Orig(n Node) Node {
	if n, ok := n.(OrigNode); ok {
		o := n.Orig()
		if o == nil {
			Dump("Orig nil", n)
			base.Fatalf("Orig returned nil")
		}
		return o
	}
	return n
}

// SepCopy返回n的一个单独的浅拷贝，
// 断开与任何其他节点的任何原始链接。
func SepCopy(n Node) Node {
	n = n.copy()
	if n, ok := n.(OrigNode); ok {
		n.SetOrig(n)
	}
	return n
}

// Copy返回n的浅拷贝。
// 如果Orig（n）==n，则Orig（Copy（n））==副本。
// 否则，也会保留原始链接。
// None
// 围绕Orig的特定语义是微妙的，但适用于大多数用途。
// 有关陷阱，请参见问题26855和27765。
func Copy(n Node) Node {
	c := n.copy()
	if n, ok := n.(OrigNode); ok && n.Orig() == n {
		c.(OrigNode).SetOrig(c)
	}
	return c
}

// DeepCopy返回n的“deep”副本，并复制其整个结构
// （除了像ONAME、OnName、OLITERAL和OTYPE这样的共享节点）。
// 如果pos.IsKnown（），则将新分配节点的源位置设置为pos。
func DeepCopy(pos src.XPos, n Node) Node {
	var edit func(Node) Node
	edit = func(x Node) Node {
		switch x.Op() {
		case OPACK, ONAME, ONONAME, OLITERAL, ONIL, OTYPE:
			return x
		}
		x = Copy(x)
		if pos.IsKnown() {
			x.SetPos(pos)
		}
		EditChildren(x, edit)
		return x
	}
	return edit(n)
}

// DeepCopyList返回列表中节点的深度副本列表（使用DeepCopy）。
func DeepCopyList(pos src.XPos, list []Node) []Node {
	var out []Node
	for _, n := range list {
		out = append(out, DeepCopy(pos, n))
	}
	return out
}
